home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / File / Find.php < prev    next >
Encoding:
PHP Script  |  2006-04-07  |  15.0 KB  |  485 lines

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2005 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license,      |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Sterling Hughes <sterling@php.net>                           |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id: Find.php,v 1.26 2006/02/11 16:28:40 techtonik Exp $
  20. //
  21.  
  22. require_once 'PEAR.php';
  23.  
  24. define('FILE_FIND_VERSION', '@package_version@');
  25.  
  26. // to debug uncomment this string
  27. // define('FILE_FIND_DEBUG', '');
  28.  
  29. /**
  30. *  Commonly needed functions searching directory trees
  31. *
  32. * @access public
  33. * @version $Id: Find.php,v 1.26 2006/02/11 16:28:40 techtonik Exp $
  34. * @package File
  35. * @author Sterling Hughes <sterling@php.net>
  36. */
  37. class File_Find
  38. {
  39.     /**
  40.      * internal dir-list
  41.      * @var array
  42.      */
  43.     var $_dirs = array();
  44.  
  45.     /**
  46.      * found files
  47.      * @var array
  48.      */
  49.     var $files = array();
  50.  
  51.     /**
  52.      * found dirs
  53.      * @var array
  54.      */
  55.     var $directories = array();
  56.  
  57.     /**
  58.      * Search the current directory to find matches for the
  59.      * the specified pattern.
  60.      *
  61.      * @param string $pattern a string containing the pattern to search
  62.      * the directory for.
  63.      *
  64.      * @param string $dirpath a string containing the directory path
  65.      * to search.
  66.      *
  67.      * @param string $pattern_type a string containing the type of
  68.      * pattern matching functions to use (can either be 'php',
  69.      * 'perl' or 'shell').
  70.      *
  71.      * @return array containing all of the files and directories
  72.      * matching the pattern or null if no matches
  73.      *
  74.      * @author Sterling Hughes <sterling@php.net>
  75.      * @access public
  76.      * @static
  77.      */
  78.     function &glob($pattern, $dirpath, $pattern_type = 'php')
  79.     {
  80.         $dh = @opendir($dirpath);
  81.  
  82.         if (!$dh) {
  83.             $pe = PEAR::raiseError("Cannot open directory");
  84.             return $pe;
  85.         }
  86.  
  87.         $match_function = File_Find::_determineRegex($pattern, $pattern_type);
  88.         $matches = array();
  89.  
  90.         // empty string cannot be specified for 'php' and 'perl' pattern
  91.         if ($pattern || ($pattern_type != 'php' && $pattern_type != 'perl')) {
  92.             while (false !== ($entry = @readdir($dh))) {
  93.                 if ($match_function($pattern, $entry) &&
  94.                     $entry != '.' && $entry != '..') {
  95.                     $matches[] = $entry;
  96.                 }
  97.             }
  98.         }
  99.  
  100.         @closedir($dh);
  101.  
  102.         if (0 == count($matches)) {
  103.             $matches = null;
  104.         }
  105.  
  106.         return $matches ;
  107.     }
  108.  
  109.     /**
  110.      * Map the directory tree given by the directory_path parameter.
  111.      *
  112.      * @param string $directory contains the directory path that you
  113.      * want to map.
  114.      *
  115.      * @return array a two element array, the first element containing a list
  116.      * of all the directories, the second element containing a list of all the
  117.      * files.
  118.      *
  119.      * @author Sterling Hughes <sterling@php.net>
  120.      * @access public
  121.      */
  122.     function &maptree($directory)
  123.     {
  124.  
  125.         /* if called statically */
  126.         if (!isset($this)  || !is_a($this, "File_Find")) {
  127.             $obj = &new File_Find();
  128.             return $obj->maptree($directory);
  129.         }
  130.       
  131.         /* clear the results just in case */
  132.         $this->files       = array();
  133.         $this->directories = array();
  134.  
  135.         /* consistency rules - strip out trailing slashes */
  136.         $directory = preg_replace('![\\\\/]+$!', '', $directory);
  137.         /* use only native system directory delimiters */
  138.         $directory = preg_replace("![\\\\/]+!", DIRECTORY_SEPARATOR, $directory);
  139.  
  140.         $this->_dirs = array($directory);
  141.  
  142.         while (count($this->_dirs)) {
  143.             $dir = array_pop($this->_dirs);
  144.             File_Find::_build($dir);
  145.             array_push($this->directories, $dir);
  146.         }
  147.  
  148.         $retval = array($this->directories, $this->files);
  149.         return $retval;
  150.  
  151.     }
  152.  
  153.     /**
  154.      * Map the directory tree given by the directory parameter.
  155.      *
  156.      * @param string $directory contains the directory path that you
  157.      * want to map.
  158.      * @param integer $maxrecursion maximun number of folders to recursive 
  159.      * map
  160.      *
  161.      * @return array a multidimensional array containing all subdirectories
  162.      * and their files. For example:
  163.      *
  164.      * Array
  165.      * (
  166.      *    [0] => file_1.php
  167.      *    [1] => file_2.php
  168.      *    [subdirname] => Array
  169.      *       (
  170.      *          [0] => file_1.php
  171.      *       )
  172.      * )
  173.      *
  174.      * @author Mika Tuupola <tuupola@appelsiini.net>
  175.      * @access public
  176.      * @static
  177.      */
  178.     function &mapTreeMultiple($directory, $maxrecursion = 0, $count = 0)
  179.     {   
  180.         $retval = array();
  181.  
  182.         $count++;
  183.  
  184.         $directory .= DIRECTORY_SEPARATOR;
  185.         
  186.         if (is_readable($directory)) {
  187.             $dh = opendir($directory);
  188.             while (false !== ($entry = @readdir($dh))) {
  189.                 if ($entry != '.' && $entry != '..') {
  190.                      array_push($retval, $entry);
  191.                 }
  192.             }
  193.             closedir($dh);
  194.         }
  195.      
  196.         while (list($key, $val) = each($retval)) {
  197.             $path = $directory . $val;
  198.             $path = str_replace(DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR,
  199.                                 DIRECTORY_SEPARATOR, $path);
  200.       
  201.             if (!is_array($val) && is_dir($path)) {
  202.                 unset($retval[$key]);
  203.                 if ($maxrecursion == 0 || $count < $maxrecursion) {
  204.                     $retval[$val] = &File_Find::mapTreeMultiple($path, 
  205.                                     $maxrecursion, $count);
  206.                 }
  207.             }
  208.         }
  209.  
  210.         return $retval;
  211.     }
  212.  
  213.     /**
  214.      * Search the specified directory tree with the specified pattern.  Return
  215.      * an array containing all matching files (no directories included).
  216.      *
  217.      * @param string $pattern the pattern to match every file with.
  218.      *
  219.      * @param string $directory the directory tree to search in.
  220.      *
  221.      * @param string $type the type of regular expression support to use, either
  222.      * 'php', 'perl' or 'shell'.
  223.      *
  224.      * @param bool $fullpath whether the regex should be matched against the
  225.      * full path or only against the filename
  226.      *
  227.      * @param string $match can be either 'files', 'dirs' or 'both' to specify
  228.      * the kind of list to return
  229.      *
  230.      * @return array a list of files matching the pattern parameter in the the
  231.      * directory path specified by the directory parameter
  232.      *
  233.      * @author Sterling Hughes <sterling@php.net>
  234.      * @access public
  235.      * @static
  236.      */
  237.     function &search($pattern, $directory, $type = 'php', $fullpath = true, $match = 'files')
  238.     {
  239.  
  240.         $matches = array();
  241.         list ($directories,$files)  = File_Find::maptree($directory);
  242.         switch($match) {
  243.             case 'directories': 
  244.                 $data = $directories; 
  245.                 break;
  246.             case 'both': 
  247.                 $data = array_merge($directories, $files); 
  248.                 break;
  249.             case 'files':
  250.             default:
  251.                 $data = $files;
  252.         }
  253.         unset($files, $directories);
  254.  
  255.         $match_function = File_Find::_determineRegex($pattern, $type);
  256.  
  257.         reset($data);
  258.         // check if empty string given (ok for 'shell' method, but bad for others)
  259.         if ($pattern || ($type != 'php' && $type != 'perl')) {
  260.             while (list(,$entry) = each($data)) {
  261.                 if ($match_function($pattern, 
  262.                                     $fullpath ? $entry : basename($entry))) {
  263.                     $matches[] = $entry;
  264.                 } 
  265.             }
  266.         }
  267.  
  268.         return $matches;
  269.     }
  270.  
  271.     /**
  272.      * Determine whether or not a variable is a PEAR error
  273.      *
  274.      * @param object PEAR_Error $var the variable to test.
  275.      *
  276.      * @return boolean returns true if the variable is a PEAR error, otherwise
  277.      * it returns false.
  278.      * @access public
  279.      */
  280.     function isError(&$var)
  281.     {
  282.         return PEAR::isError($var);
  283.     }
  284.  
  285.     /**
  286.      * internal function to build singular directory trees, used by
  287.      * File_Find::maptree()
  288.      *
  289.      * @param string $directory name of the directory to read
  290.      * @return void
  291.      */
  292.     function _build($directory)
  293.     {
  294.  
  295.         $dh = @opendir($directory);
  296.  
  297.         if (!$dh) {
  298.             $pe = PEAR::raiseError("Cannot open directory");
  299.             return $pe;
  300.         }
  301.  
  302.         while (false !== ($entry = @readdir($dh))) {
  303.             if ($entry != '.' && $entry != '..') {
  304.  
  305.                 $entry = $directory.DIRECTORY_SEPARATOR.$entry;
  306.                 $entry = str_replace(DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR,
  307.                                      DIRECTORY_SEPARATOR, $entry);
  308.  
  309.                 if (is_dir($entry)) {
  310.                     array_push($this->_dirs, $entry);
  311.                 } else {
  312.                     array_push($this->files, $entry);
  313.                 }
  314.             }
  315.         }
  316.  
  317.         @closedir($dh);
  318.     }
  319.  
  320.     /**
  321.      * internal function to determine the type of regular expression to
  322.      * use, implemented by File_Find::glob() and File_Find::search()
  323.      *
  324.      * @param string $type given RegExp type
  325.      * @return string kind of function ( "eregi", "ereg" or "preg_match") ;
  326.      *
  327.      */
  328.     function _determineRegex($pattern, $type)
  329.     {
  330.         if (!strcasecmp($type, 'shell')) {
  331.             $match_function = 'File_Find_match_shell';
  332.         } else if (!strcasecmp($type, 'perl')) {
  333.             $match_function = 'preg_match';
  334.         } else if (!strcasecmp(substr($pattern, -2), '/i')) {
  335.             $match_function = 'eregi';
  336.         } else {
  337.             $match_function = 'ereg';
  338.         }
  339.         return $match_function;
  340.     }
  341.  
  342. }
  343.  
  344. /**
  345. * Package method to match via 'shell' pattern. Provided in global
  346. * scope, because they should be called like 'preg_match' and 'eregi'
  347. * and can be easily copied into other packages
  348. *
  349. * @author techtonik <techtonik@php.net>
  350. * @return mixed bool on success and PEAR_Error on failure
  351. */ 
  352. function File_Find_match_shell($pattern, $filename)
  353. {
  354.     // {{{ convert pattern to positive and negative regexps
  355.         $positive = $pattern;
  356.         $negation = substr_count($pattern, "|");
  357.  
  358.         if ($negation > 1) {
  359.             PEAR::raiseError("Mask string contains errors!");
  360.             return FALSE;
  361.         } elseif ($negation) {
  362.             list($positive, $negative) = explode("|", $pattern);
  363.             if (strlen($negative) == 0) {
  364.                 PEAR::raiseError("File-mask string contains errors!");
  365.                 return FALSE;
  366.             }
  367.         }
  368.  
  369.        $positive = _File_Find_match_shell_get_pattern($positive);
  370.        if ($negation) {
  371.            $negative = _File_Find_match_shell_get_pattern($negative);
  372.        }
  373.     // }}} convert end 
  374.  
  375.  
  376.     if (defined("FILE_FIND_DEBUG")) {
  377.         print("Method: $type\nPattern: $pattern\n Converted pattern:");
  378.         print_r($positive);
  379.         if (isset($negative)) print_r($negative);
  380.     }
  381.  
  382.     if (!preg_match($positive, $filename)) {
  383.         return FALSE;
  384.     } else {
  385.         if (isset($negative) 
  386.               && preg_match($negative, $filename)) {
  387.             return FALSE;
  388.         } else {
  389.             return TRUE;
  390.         }
  391.     }
  392. }
  393.  
  394. /**
  395. * function used by File_Find_match_shell to convert 'shell' mask
  396. * into pcre regexp. Some of the rules (see testcases for more): 
  397. *  escaping all special chars and replacing 
  398. *    . with \.
  399. *    * with .*
  400. *    ? with .{1}
  401. *    also adding ^ and $ as the pattern matches whole filename
  402. *
  403. * @author techtonik <techtonik@php.net>
  404. * @return string pcre regexp for preg_match
  405. */ 
  406. function _File_Find_match_shell_get_pattern($mask) {
  407.     // get array of several masks (if any) delimited by comma
  408.     // do not touch commas in char class
  409.     $premasks = preg_split("|(\[[^\]]+\])|", $mask, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
  410.     if (defined("FILE_FIND_DEBUG")) {
  411.         print("\nPremask: ");
  412.         print_r($premasks);
  413.     }
  414.     $pi = 0;
  415.     foreach($premasks as $pm) {
  416.         if (!isset($masks[$pi])) $masks[$pi] = "";
  417.         if ($pm{0} == '[' && $pm{strlen($pm)-1} == ']') {
  418.             // strip commas from character class
  419.             $masks[$pi] .= str_replace(",", "", $pm);
  420.         } else {
  421.             $tarr = explode(",", $pm);
  422.             if (sizeof($tarr) == 1) {
  423.                 $masks[$pi] .= $pm;
  424.             } else {
  425.                 foreach ($tarr as $te) {
  426.                     $masks[$pi++] .= $te;
  427.                     $masks[$pi] = "";
  428.                 }
  429.                 unset($masks[$pi--]);
  430.             }
  431.         }
  432.     }
  433.  
  434.     // if empty string given return *.* pattern
  435.     if (strlen($mask) == 0) return "!^.*$!";
  436.  
  437.     // convert to preg regexp
  438.     $regexmask = implode("|", $masks);
  439.     if (defined("FILE_FIND_DEBUG")) {
  440.         print("regexMask step one(implode): $regexmask");
  441.     }
  442.     $regexmask = addcslashes($regexmask, '^$}!{)(\/.+');
  443.     if (defined("FILE_FIND_DEBUG")) {
  444.         print("\nregexMask step two(addcslashes): $regexmask");
  445.     }
  446.     $regexmask = preg_replace("!(\*|\?)!", ".$1", $regexmask);
  447.     if (defined("FILE_FIND_DEBUG")) {
  448.         print("\nregexMask step three(* ? -> .* .?): $regexmask");
  449.     }
  450.     // a special case '*.' at the end means that there is no extension
  451.     $regexmask = preg_replace("!\.\*\\\.(\||$)!", "[^\.]*$1", $regexmask);
  452.     // it is impossible to have dot at the end of filename
  453.     $regexmask = preg_replace("!\\\.(\||$)!", "$1", $regexmask);
  454.     // and .* at the end also means that there could be nothing at all
  455.     //   (i.e. no dot at the end also)
  456.     $regexmask = preg_replace("!\\\.\.\*(\||$)!", "(\\\\..*)?$1", $regexmask);
  457.     if (defined("FILE_FIND_DEBUG")) {
  458.         print("\nregexMask step two and half(*.$ \\..*$ .$ -> [^.]*$ .?.* $): $regexmask");
  459.     }
  460.     // if no extension supplied - add .* to match partially from filename start
  461.     if (strpos($regexmask, "\\.") === FALSE) $regexmask .= ".*";
  462.  
  463.     // file mask match whole name - adding restrictions
  464.     $regexmask = preg_replace("!(\|)!", '^'."$1".'$', $regexmask);
  465.     $regexmask = '^'.$regexmask.'$';
  466.     if (defined("FILE_FIND_DEBUG")) {
  467.         print("\nregexMask step three(^ and $ to match whole name): $regexmask");
  468.     }
  469.     // wrap regex into ! since all ! are already escaped
  470.     $regexmask = "!$regexmask!i";
  471.     if (defined("FILE_FIND_DEBUG")) {
  472.         print("\nWrapped regex: $regexmask\n");
  473.     }
  474.     return $regexmask;
  475. }
  476.  
  477. /*
  478.  * Local variables:
  479.  * tab-width: 4
  480.  * c-basic-offset: 4
  481.  * End:
  482.  */
  483.  
  484. ?>
  485.